package gajdulewiczr;

import java.util.ArrayList;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.GLUquadric;

import com.sun.opengl.util.GLUT;
import com.sun.opengl.util.texture.Texture;

/*
 * Implemented:
 * Fog: 2
 * MipMapping: 4
 * Multis: 2
 * Odbicia: 5
 * Skybox: 4
 */

public class CopyOfMyGLListener implements GLEventListener {

	// consts
	private int floorLen = 20;
	private final double TICK_DELTA = 0.01;
	private final double MOVE_SPEED = 0.3f;
	// camera
	double xpos = 8, ypos = 10, zpos = 32;
	double xrot = 0, yrot = -10;
	// tick
	private double radians = 0;
	private float angles = 0;

	// fps measuer
	private int frames;
	private long start;

	// point light
	float px = 1, py = 6, pz = 11;
	// mesh
	ArrayList<ObjMesh> objects = new ArrayList<ObjMesh>();
	TextureProvider texts = new TextureProvider();
	TerrainProvider terrP = new TerrainProvider();
	float[][] terr = terrP.nextTerrain();
	float[][][] terrN = calcTerrNormals(terr);
	float hscale = 0.15f;
	float vscale = 1f;
	// effects
	// fog
	private boolean fog;
	private float fogIntensity;
	// skybox
	private boolean skybox = false;
	// sphere
	private GLUquadric quadric;
	private boolean linear = true, trilinear = true;
	private boolean lodOn;
	private float lod = 1f;
	// multis
	private boolean multis;
	private boolean terrainOn;

	@Override
	public void display(GLAutoDrawable drawable) {
		if (frames == 0)
			start = System.currentTimeMillis();
		GL gl = drawable.getGL();
		GLU glu = new GLU();
		GLUT glut = new GLUT();
		tick();
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glPushMatrix();
		// view
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
		if (skybox)
			drawSkybox(gl, glu);
		setupCamera(gl);
		// setup lights
		setupLights(gl);
		drawBottom(gl, glut, skybox);
		if (multis)
			gl.glEnable(GL.GL_MULTISAMPLE);
		else
			gl.glDisable(GL.GL_MULTISAMPLE);
		drawFog(gl, fogIntensity);
		// drawLight(gl, glu, glut);
		drawSnowMan(gl, glut);
		drawEarth(gl, glu);
		if (terrainOn)
			drawTerrain(gl, glut);
		drawPointLight(gl, glut);
		// drawBackground(drawable);
		drawChristmasTree(gl, glu, glut);
		for (ObjMesh obj : objects) {
			drawModel(gl, obj);
		}

		drawTeapot(gl, glut);
		drawGeometric(gl, glut);
		gl.glPopMatrix();
		gl.glFlush();
		if (frames == 1000) {
			frames = 0;
			long end = System.currentTimeMillis();
			System.out.println("FPS: " + 1000 / ((end - start) / 1000.0f));
		} else
			frames++;
	}

	@Override
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {
		// TODO Auto-generated methodq stub

	}

	@Override
	public void init(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		GLU glu = new GLU();
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(60.0f, 800f / 600f, 1f, 500.0f);
		// depth test
		gl.glEnable(GL.GL_DEPTH_TEST);
		gl.glDepthFunc(GL.GL_LEQUAL);
		// lights
		gl.glEnable(GL.GL_LIGHT0);
		gl.glEnable(GL.GL_LIGHT1);
		gl.glEnable(GL.GL_LIGHTING);
		// normals
		gl.glEnable(GL.GL_AUTO_NORMAL);
		// sphere
		quadric = glu.gluNewQuadric();
		texts.loadCubeMap(gl);
		glu.gluQuadricTexture(quadric, true); // creates texture coords
		objects.add(new ObjMesh("obj/gourd.obj", 0.5f,
				new float[] { 0, 5, -8 }, new float[] { 90, 90, -90 }));
		// objects.add(new ObjMesh("obj/humanoid_tri.obj", 3f, new float[] { 9,
		// 0,
		// 0 }, new float[] { 90, 180, 45 }));
		texts.loadTexture("earthff", "earth.jpg", false, false);
		texts.loadTexture("earthft", "earth.jpg", false, true);
		texts.loadTexture("earthtf", "earth.jpg", true, false);
		texts.loadTexture("earthtt", "earth.jpg", true, true);
	}

	@Override
	public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3,
			int arg4) {

	}

	/** drawAxis */
	@SuppressWarnings("unused")
	private void drawAxis(GLAutoDrawable drawable) {
		GL gl = drawable.getGL();
		double axisLength;
		axisLength = 15;
		gl.glBegin(GL.GL_LINES);
		// draw x axis in red
		gl.glColor3d(1, 0, 0);
		gl.glVertex3d(0, 0, 0);
		gl.glVertex3d(axisLength, 0, 0);
		// draw y axis in green
		gl.glColor3d(0, 1, 0);
		gl.glVertex3d(0, 0, 0);
		gl.glVertex3d(0, axisLength, 0);
		// draw z axs in blue
		gl.glColor3d(0, 0, 1);
		gl.glVertex3d(0, 0, 0);
		gl.glVertex3d(0, 0, axisLength);
		gl.glEnd();
	}

	private void drawBottom(GL gl, GLUT glut, boolean wireframe) {
		gl.glPushMatrix();
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_AMBIENT, new float[] {
				1.0f, 0f, 0f, 1.0f }, 0);
		if (wireframe) {
			gl.glBegin(GL.GL_LINES);
			float floorunit = floorLen / 5;
			float fflen = (float) floorLen;
			for (int i = 0; i < 11; i++) {
				gl.glVertex3f(-fflen + floorunit * i, 0f, -fflen);
				gl.glVertex3f(-fflen + floorunit * i, 0f, +fflen);
				gl.glVertex3f(-fflen, 0f, -fflen + floorunit * i);
				gl.glVertex3f(+fflen, 0f, -fflen + floorunit * i);
			}

		} else {
			gl.glBegin(GL.GL_QUADS);
			gl.glVertex3f(-floorLen, 0, -floorLen);
			// gl.glTexCoord3f(-floorLen, 0, -floorLen);
			gl.glVertex3f(-floorLen, 0, floorLen);
			// gl.glTexCoord3f(-floorLen, 0, floorLen);
			gl.glVertex3f(floorLen, 0, floorLen);
			// gl.glTexCoord3f(floorLen, 0, floorLen);
			gl.glVertex3f(floorLen, 0, -floorLen);
			// gl.glTexCoord3f(floorLen, 0, -floorLen);
		}
		gl.glEnd();
		gl.glPopMatrix();
	}

	private void drawChristmasTree(GL gl, GLU glu, GLUT glut) {
		gl.glPushMatrix();
		float[] matAmb = { 0.0f, 0.5f, 0.1f, 1.0f };
		float[] matDiff = { 0.0f, 0.5f, 0.1f, 1.0f };
		float[] matSpec = { 0.0f, 0.5f, 0.1f, 1.0f };
		float shineyVal = 128.0f;
		// setup material
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmb, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiff, 0);
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, matSpec, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, shineyVal);
		// gl.glColor3d(1.0,0.0,1.0);
		gl.glTranslated(px, 0, pz);
		gl.glRotated(-90, 1, 0, 0);
		GLUquadric quad = glu.gluNewQuadric();
		glu.gluQuadricNormals(quad, GLU.GLU_SMOOTH); // Create Smooth Normals
		glu.gluCylinder(quad, 0.2f, 0.2f, 2f, 40, 40);
		gl.glPopMatrix();
		gl.glPushMatrix();
		gl.glTranslated(px, 2.0f, pz);
		gl.glRotated(-90, 1, 0, 0);
		glut.glutSolidCone(2f, 2f, 40, 40);
		gl.glPopMatrix();
		gl.glPushMatrix();
		gl.glTranslated(px, 3.8f, pz);
		gl.glRotated(-90, 1, 0, 0);
		glut.glutSolidCone(1.5f, 1.6f, 30, 30);
		gl.glPopMatrix();
		gl.glPushMatrix();
		gl.glTranslated(px, 5f, pz);
		gl.glRotated(-90, 1, 0, 0);
		glut.glutSolidCone(1.2f, 1f, 30, 30);
		gl.glPopMatrix();
	}

	private void drawEarth(GL gl, GLU glu)
	// draw the earth orbiting a point, and rotating around its y-axis
	{
		// enable texturing and choose the 'earth' texture
		gl.glEnable(GL.GL_TEXTURE_2D);
		Texture earthTex;
		if (linear) {
			if (trilinear)
				earthTex = texts.getTexture("earthtt");
			else
				earthTex = texts.getTexture("earthtf");
		} else {
			if (trilinear)
				earthTex = texts.getTexture("earthft");
			else
				earthTex = texts.getTexture("earthff");
		}
		if (lodOn)
			gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_LOD_BIAS, lod);
		earthTex.bind();
		// set how the sphere's surface responds to the light
		gl.glPushMatrix();
		float[] grayCol = { 0.8f, 0.8f, 0.8f, 1.0f };
		// float[] blueCol = {0.0f, 0.0f, 0.8f, 1.0f};
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT_AND_DIFFUSE, grayCol, 0);

		float[] whiteCol = { 1.0f, 1.0f, 1.0f, 1.0f };
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, whiteCol, 0);
		gl.glMateriali(GL.GL_FRONT, GL.GL_SHININESS, 100);

		gl.glTranslatef(7.0f, 7.0f, -7.0f); // position the sphere
		// y-axis
		// units
		gl.glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		// rotate sphere upwards around x-axis so texture is correctly
		// orientated
		gl.glRotatef(angles, 0.0f, 0.0f, 1.0f); // spin around z-axis (which
		// looks like y-axis)

		glu.gluSphere(quadric, 5.0f, 50, 50); // generate the textured sphere
		// radius, slices, stacks
		gl.glPopMatrix();

		gl.glDisable(GL.GL_TEXTURE_2D);
	}

	private void drawFog(GL gl, float intensity) {
		float[] fogColor = { 0.5f, 0.5f, 0.5f };
		float fogDensity = intensity;
		float fogStart = 2f;
		float fogEnd = 15f;
		gl.glFogfv(GL.GL_FOG_COLOR, fogColor, 0);
		gl.glFogi(GL.GL_FOG_MODE, GL.GL_EXP2);
		gl.glFogf(GL.GL_FOG_DENSITY, fogDensity);
		gl.glHint(GL.GL_FOG_HINT, GL.GL_DONT_CARE);
		gl.glFogf(GL.GL_FOG_START, fogStart);
		gl.glFogf(GL.GL_FOG_END, fogEnd);
		if (fog)
			gl.glEnable(GL.GL_FOG);
		else
			gl.glDisable(GL.GL_FOG);
	}

	private void drawGeometric(GL gl, GLUT glut) {

		gl.glPushMatrix();
		float[] matAmb = { 0.1f, 0.1f, 0.1f, 1.0f };
		float[] matDiff = { 0.8f, 0.1f, 0.1f, 1.0f };
		float[] matSpec = { 1.0f, 1.0f, 1.0f, 1.0f };
		float shineyVal = 18.0f;
		// setup material
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmb, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiff, 0);
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, matSpec, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, shineyVal);
		// gl.glColor3d(1.0,0.0,1.0);
		gl.glTranslated(1.5 * Math.sin(radians), 10.0, 2.0 * Math.cos(radians));
		gl.glRotated(-450 * Math.sin(radians), 0, 1.0, 0.0);
		glut.glutSolidTorus(0.2f, 1.2f, 50, 50);
		gl.glPopMatrix();
	}

	@SuppressWarnings("unused")
	private void drawLight(GL gl, GLU glu, GLUT glut) {
		gl.glPushMatrix();
		float[] matAmb = { 1f, 1f, 1f, 0.5f };
		float[] matDiff = { 0.3f, 0.3f, 0.3f, 0.5f };
		float[] matSpec = { 1.0f, 1.0f, 1.0f, 0.5f };
		// float shineyVal = 128.0f;
		// setup material
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmb, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiff, 0);
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, matSpec, 0);
		// gl.glMaterialfv(GL.GL_FRONT, GL.GL_EMISSION, new float[]
		// {0.2f,0.2f,0.2f,1f},0);
		// gl.glColor3d(1.0,0.0,1.0);
		gl.glTranslatef(1, 14, 1);
		gl.glRotatef(90, 1, 0, 0);
		glut.glutSolidCone(0.3, 10, 10, 10);
		gl.glPopMatrix();
	}

	private void drawModel(GL gl, ObjMesh obj) {
		gl.glPushMatrix();
		float[] matAmb = { 1.0f, 1, 0, 1.0f };
		float[] matDiff = { 1.0f, 1, 0, 1.0f };
		float[] matSpec = { 1.0f, 1, 0, 1.0f };
		float shineyVal = 128.0f;
		// setup material
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmb, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiff, 0);
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, matSpec, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, shineyVal);
		// gl.glColor3d(1.0,1.0,1.0);
		// gl.glTranslated(7.0 * Math.cos(radians), 2.0, 10.0 *
		// Math.sin(radians));
		gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_S,
				GL.GL_REPEAT);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_T,
				GL.GL_REPEAT);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_R,
				GL.GL_REPEAT);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MAG_FILTER,
				GL.GL_NEAREST);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MIN_FILTER,
				GL.GL_NEAREST);
		gl.glEnable(GL.GL_TEXTURE_CUBE_MAP);
		gl.glTexGeni(GL.GL_S, GL.GL_TEXTURE_GEN_MODE, GL.GL_REFLECTION_MAP);
		gl.glTexGeni(GL.GL_T, GL.GL_TEXTURE_GEN_MODE, GL.GL_REFLECTION_MAP);
		gl.glTexGeni(GL.GL_R, GL.GL_TEXTURE_GEN_MODE, GL.GL_REFLECTION_MAP);
		gl.glEnable(GL.GL_TEXTURE_GEN_S);
		gl.glEnable(GL.GL_TEXTURE_GEN_T);
		gl.glEnable(GL.GL_TEXTURE_GEN_R);
		gl.glTranslatef(obj.x, obj.y, obj.z);
		gl.glRotatef(obj.xrot, 1, 0, 0);
		gl.glRotatef(obj.yrot, 0, 1, 0);
		gl.glRotatef(obj.zrot, 0, 0, 1);
		gl.glBegin(GL.GL_TRIANGLES);
		for (int i = 0; i < obj.faces.length; i++) {

			for (int j = 0; j < obj.faces[i].length; j++) {
				float[] vert = obj.vertices[obj.faces[i][j]];
				float[] vnorm = obj.vnormals[obj.faces[i][j]];
				gl.glNormal3f(vnorm[0], vnorm[1], vnorm[2]);
				gl.glVertex3f(vert[0], vert[1], vert[2]);
				gl.glTexCoord3f(vert[0], vert[1], vert[2]);
			}

		}
		gl.glEnd();
		gl.glDisable(GL.GL_TEXTURE_GEN_S);
		gl.glDisable(GL.GL_TEXTURE_GEN_T);
		gl.glDisable(GL.GL_TEXTURE_GEN_R);
		gl.glDisable(GL.GL_TEXTURE_CUBE_MAP);
		gl.glPopMatrix();

	}

	private void drawPointLight(GL gl, GLUT glut) {
		float[] all = { 1f, 1f, 1f, 1f };
		gl.glPushMatrix();
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, all, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, all, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, all, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, 128f);
		gl.glTranslated(px, py, pz);
		glut.glutSolidSphere(.1f, 15, 15);
		gl.glPopMatrix();
	}

	private void drawSkybox(GL gl, GLU glu) {
		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glRotated(xrot, 1.0, 0.0, 0.0); // rotate our camera on teh x-axis
		// (left and right)
		gl.glRotated(yrot, 0.0, 1.0, 0.0); // rotate our camera on the y-axis
		gl.glPushAttrib(GL.GL_ENABLE_BIT);
		gl.glDisable(GL.GL_DEPTH_TEST);
		gl.glDisable(GL.GL_LIGHTING);
		gl.glDisable(GL.GL_BLEND);
		gl.glDepthMask(false);
		gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_S,
				GL.GL_CLAMP_TO_EDGE);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_T,
				GL.GL_CLAMP_TO_EDGE);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MAG_FILTER,
				GL.GL_LINEAR);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MIN_FILTER,
				GL.GL_LINEAR);
		gl.glEnable(GL.GL_TEXTURE_CUBE_MAP);
		gl.glDisable(GL.GL_TEXTURE_GEN_S);
		gl.glDisable(GL.GL_TEXTURE_GEN_T);
		gl.glDisable(GL.GL_TEXTURE_GEN_R);
		gl.glBegin(GL.GL_QUADS);
		gl.glColor3d(1, 1, 1);

		/** Front Face **/
		// gl.glTexCoord3f(0.25f, 0);
		gl.glTexCoord3f(-200f, 200f, -200f);
		gl.glVertex3f(-200f, 200f, -200f);

		// gl.glTexCoord3f(0.25f, 0.25f);
		gl.glTexCoord3f(-200f, -200f, -200f);
		gl.glVertex3f(-200f, -200f, -200f);

		// gl.glTexCoord3f(200f, 0.25f);
		gl.glTexCoord3f(200f, -200f, -200f);
		gl.glVertex3f(200f, -200f, -200f);

		// gl.glTexCoord3f(200f, 0);
		gl.glTexCoord3f(200f, 200f, -200f);
		gl.glVertex3f(200f, 200f, -200f);

		/** Back Face **/
		// gl.glTexCoord3f(0.25f, 0.75f);
		gl.glTexCoord3f(-200f, 200f, 200f);
		gl.glVertex3f(-200f, 200f, 200f);

		// gl.glTexCoord3f(0.25f, 200f);
		gl.glTexCoord3f(-200f, -200f, 200f);
		gl.glVertex3f(-200f, -200f, 200f);

		// gl.glTexCoord3f(200f, 200f);

		gl.glTexCoord3f(200f, -200f, 200f);
		gl.glVertex3f(200f, -200f, 200f);
		// gl.glTexCoord3f(200f, 0.75f);

		gl.glTexCoord3f(200f, 200f, 200f);
		gl.glVertex3f(200f, 200f, 200f);
		/** Left Face **/
		// gl.glTexCoord3f(0, 0.25f);
		gl.glTexCoord3f(-200f, 200f, -200f);
		gl.glVertex3f(-200f, 200f, -200f);

		// gl.glTexCoord3f(0.25f, 0.25f);
		gl.glTexCoord3f(-200f, -200f, -200f);
		gl.glVertex3f(-200f, -200f, -200f);

		// gl.glTexCoord3f(0.25f, 200f);

		gl.glTexCoord3f(-200f, -200f, 200f);
		gl.glVertex3f(-200f, -200f, 200f);
		// gl.glTexCoord3f(0, 200f);

		gl.glTexCoord3f(-200f, 200f, 200f);
		gl.glVertex3f(-200f, 200f, 200f);
		/** Right Face **/
		// gl.glTexCoord3f(0.75f, 0.25f);

		gl.glTexCoord3f(200f, 200f, -200f);
		gl.glVertex3f(200f, 200f, -200f);
		// gl.glTexCoord3f(200f, 0.25f);

		gl.glTexCoord3f(200f, -200f, -200f);
		gl.glVertex3f(200f, -200f, -200f);
		// gl.glTexCoord3f(200f, 200f);
		gl.glTexCoord3f(200f, -200f, 200f);
		gl.glVertex3f(200f, -200f, 200f);

		// gl.glTexCoord3f(0.75f, 200f);
		gl.glTexCoord3f(200f, 200f, 200f);
		gl.glVertex3f(200f, 200f, 200f);

		/** Top Face **/
		// gl.glTexCoord3f(1, 0.25f);
		gl.glTexCoord3f(-200f, 200f, -200f);
		gl.glVertex3f(-200f, 200f, -200f);

		// gl.glTexCoord3f(0.75f, 0.25f);
		gl.glTexCoord3f(200f, 200f, -200f);
		gl.glVertex3f(200f, 200f, -200f);

		// gl.glTexCoord3f(0.75f, 200f);
		gl.glTexCoord3f(200f, 200f, 200f);
		gl.glVertex3f(200f, 200f, 200f);

		// gl.glTexCoord3f(1, 200f);
		gl.glTexCoord3f(-200f, 200f, 200f);
		gl.glVertex3f(-200f, 200f, 200f);

		/** Bottom Face **/
		// gl.glTexCoord3f(200f, 200f);
		gl.glTexCoord3f(200f, -200f, 200f);
		gl.glVertex3f(200f, -200f, 200f);

		// gl.glTexCoord3f(0.25f, 200f);
		gl.glTexCoord3f(-200f, -200f, 200f);
		gl.glVertex3f(-200f, -200f, 200f);

		// gl.glTexCoord3f(0.25f, 0.25f);
		gl.glTexCoord3f(-200f, -200f, -200f);
		gl.glVertex3f(-200f, -200f, -200f);

		// gl.glTexCoord3f(200f, 0.25f);
		gl.glTexCoord3f(200f, -200f, -200f);
		gl.glVertex3f(200f, -200f, -200f);

		gl.glEnd();
		gl.glDisable(GL.GL_TEXTURE_GEN_S);
		gl.glDisable(GL.GL_TEXTURE_GEN_T);
		gl.glDisable(GL.GL_TEXTURE_GEN_R);
		gl.glDisable(GL.GL_TEXTURE_CUBE_MAP);
		gl.glDepthMask(true);
		gl.glPopAttrib();
		gl.glPopMatrix();

	}

	private void drawSnowMan(GL gl, GLUT glut) {
		gl.glPushMatrix();
		float[] matAmb = { 0.3f, 0.3f, 0.3f, 1.0f };
		float[] matDiff = { 0.5f, 0.5f, 0.5f, 1.0f };
		float[] matSpec = { 0.0f, 0.0f, 0.0f, 1.0f };
		float shineyVal = 0.0f;
		// setup material
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmb, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiff, 0);
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, matSpec, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, shineyVal);
		// gl.glColor3d(1.0,0.0,1.0);
		gl.glTranslated(-3.0f, 1.5f, 3.0f);
		glut.glutSolidSphere(2f, 30, 30);
		gl.glTranslated(0, 2.5f, 0f);
		glut.glutSolidSphere(1.5f, 30, 30);
		gl.glTranslated(0, 2f, 0f);
		glut.glutSolidSphere(1f, 30, 30);
		gl.glPopMatrix();

	}

	private void drawTeapot(GL gl, GLUT glut) {
		gl.glPushMatrix();
		float[] matAmb = { 1.0f, 1.0f, 1.0f, 1.0f };
		float[] matDiff = { 1.0f, 1.0f, 1.0f, 1.0f };
		float[] matSpec = { 1.0f, 1.0f, 1.0f, 1.0f };
		float shineyVal = 128.0f;
		// setup material
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmb, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiff, 0);
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, matSpec, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, shineyVal);
		// gl.glColor3d(1.0,1.0,1.0);
		// gl.glTranslated(7.0 * Math.cos(radians), 2.0, 10.0 *
		// Math.sin(radians));
		gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_S,
				GL.GL_REPEAT);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_T,
				GL.GL_REPEAT);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_R,
				GL.GL_REPEAT);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MAG_FILTER,
				GL.GL_NEAREST);
		gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MIN_FILTER,
				GL.GL_NEAREST);
		gl.glEnable(GL.GL_TEXTURE_CUBE_MAP);
		gl.glTexGeni(GL.GL_S, GL.GL_TEXTURE_GEN_MODE, GL.GL_REFLECTION_MAP);
		gl.glTexGeni(GL.GL_T, GL.GL_TEXTURE_GEN_MODE, GL.GL_REFLECTION_MAP);
		gl.glTexGeni(GL.GL_R, GL.GL_TEXTURE_GEN_MODE, GL.GL_REFLECTION_MAP);
		gl.glEnable(GL.GL_TEXTURE_GEN_S);
		gl.glEnable(GL.GL_TEXTURE_GEN_T);
		gl.glEnable(GL.GL_TEXTURE_GEN_R);
		gl.glTranslated(7.0, 2.0, 10.0);
		gl.glRotated(-Math.toDegrees((2.0 * radians)), 1.0, 1.0, 0.0);
		glut.glutSolidTeapot(1.0f);
		gl.glDisable(GL.GL_TEXTURE_GEN_S);
		gl.glDisable(GL.GL_TEXTURE_GEN_T);
		gl.glDisable(GL.GL_TEXTURE_GEN_R);
		gl.glDisable(GL.GL_TEXTURE_CUBE_MAP);
		gl.glPopMatrix();
	}

	private void drawTerrain(GL gl, GLUT glut) {
		gl.glPushMatrix();
		// gl.glDisable(GL.GL_LIGHTING);
		float[] matAmb = { 0.3f, 0.3f, 0.3f, 1.0f };
		float[] matDiff = { 0.8f, 0.8f, 0.8f, 1.0f };
		float[] matSpec = { 0.8f, 0.8f, 0.8f, 1.0f };
		float shineyVal = 100.0f;
		// setup material
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, matAmb, 0);
		gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, matDiff, 0);
		gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GL.GL_SPECULAR, matSpec, 0);
		gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, shineyVal);
		gl.glTranslated(-12, 1, 12);
		gl.glFrontFace(GL.GL_CCW);
		gl.glCullFace(GL.GL_BACK);
		gl.glEnable(GL.GL_CULL_FACE);
		int terrSize = terr.length;
		float hscale = 0.15f;
		float vscale = 1f;

		for (int i = 0; i < terrSize - 1; i++) {
			gl.glBegin(GL.GL_TRIANGLE_STRIP);
			for (int j = 0; j < terrSize - 1; j++) {
				 //setColor(gl, terr[i][j]);
				gl.glNormal3f(terrN[i][j][0], terrN[i][j][1], terrN[i][j][2]);
				gl.glVertex3d(i * hscale, terr[i][j] * vscale, j * hscale);
				 //setColor(gl, terr[i][j + 1]);
				gl.glNormal3f(terrN[i][j + 1][0], terrN[i][j + 1][1],
						terrN[i][j + 1][2]);
				gl.glVertex3d(i * hscale, terr[i][j + 1] * vscale, (j + 1)
						* hscale);
				 //setColor(gl, terr[i + 1][j]);
				gl.glNormal3f(terrN[i + 1][j][0], terrN[i + 1][j][1],
						terrN[i + 1][j][2]);
				gl.glVertex3d((i + 1) * hscale, terr[i + 1][j] * vscale, j
						* hscale);
				 //setColor(gl, terr[i + 1][j + 1]);
				gl.glNormal3f(terrN[i + 1][j + 1][0], terrN[i + 1][j + 1][1],
						terrN[i + 1][j + 1][2]);
				gl.glVertex3d((i + 1) * hscale, terr[i + 1][j + 1] * vscale,
						(j + 1) * hscale);
			}
			gl.glEnd();
		}
		gl.glDisable(GL.GL_CULL_FACE);
		gl.glEnable(GL.GL_LIGHTING);
		gl.glPopMatrix();
	}

	private void setColor(GL gl, float f) {
		float color = f * f;
		gl.glColor3f(color, color, color);
	}

	private void setupCamera(GL gl) {
		gl.glRotated(xrot, 1.0, 0.0, 0.0); // rotate our camera on teh x-axis
		// (left and right)
		gl.glRotated(yrot, 0.0, 1.0, 0.0); // rotate our camera on the y-axis
		// (up and down)
		gl.glTranslated(-xpos, -ypos, -zpos); // translate the screen
	}

	private void setupLights(GL gl) {
		// light 0, directional
		float[] ambientColor = { 0.1f, 0.1f, 0.1f, 1.0f }; // ambient light
		float[] diffuseColor = { 0.3f, 0.3f, 0.3f, 1.0f }; // diffuse color,
		float[] specColor = { 0.2f, 0.2f, 0.f, 1.0f };
		float[] position = { 0.0f, 10.0f, 0.0f, 0.0f }; // position light, w = 0

		gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, ambientColor, 0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, diffuseColor, 0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, specColor, 0);
		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, position, 0);
		// 
		float[] ambientColor1 = { 0.0f, 0.1f, 0.1f, 1.0f }; // ambient light
		// array, got
		// nothing
		float[] diffuseColor1 = { 0.8f, 0.8f, 0.0f, 1.0f }; // diffuse color,
		// straight red
		float[] specColor1 = { 0.8f, 0.8f, 0.0f, 0.0f };
		float[] position1 = { px, py, pz, 1.0f }; // position light
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_AMBIENT, ambientColor1, 0);
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_DIFFUSE, diffuseColor1, 0);
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_SPECULAR, specColor1, 0);
		gl.glLightfv(GL.GL_LIGHT1, GL.GL_POSITION, position1, 0);
		// gl.glLightf(GL.GL_LIGHT1, GL.GL_SPOT_CUTOFF, 100);
	}

	private void tick() {
		angles += 0.25f;
		radians += TICK_DELTA;
		if (radians > 2 * Math.PI)
			radians = 0;
		if (angles == 360)
			angles = 0;
	}

	public void moveCameraRight() {
		double yrotrad;
		yrotrad = (yrot / 180 * Math.PI);
		xpos += (Math.cos(yrotrad)) * MOVE_SPEED;
		zpos += (Math.sin(yrotrad)) * MOVE_SPEED;
		// printCamPos();
	}

	public void moveCameraLeft() {
		double yrotrad;
		yrotrad = (yrot / 180 * Math.PI);
		xpos -= (Math.cos(yrotrad)) * MOVE_SPEED;
		zpos -= (Math.sin(yrotrad)) * MOVE_SPEED;
		// printCamPos();
	}

	public void moveCameraForward() {
		double xrotrad, yrotrad;
		yrotrad = (yrot / 180 * Math.PI);
		xrotrad = (xrot / 180 * Math.PI);
		xpos += (Math.sin(yrotrad) * Math.cos(xrotrad)) * MOVE_SPEED;
		ypos -= (Math.sin(xrotrad)) * MOVE_SPEED;
		zpos -= (Math.cos(yrotrad) * Math.cos(xrotrad)) * MOVE_SPEED;

	}

	public void moveCameraBackward() {
		double xrotrad, yrotrad;
		yrotrad = (yrot / 180 * Math.PI);
		xrotrad = (xrot / 180 * Math.PI);
		xpos -= (Math.sin(yrotrad) * Math.cos(xrotrad)) * MOVE_SPEED;
		ypos += (Math.sin(xrotrad)) * MOVE_SPEED;
		zpos += (Math.cos(yrotrad) * Math.cos(xrotrad)) * MOVE_SPEED;
	}

	public void lookUp() {
		if (xrot > -90)
			xrot--;
		else
			xrot = -90;
		// printCamPos();
	}

	public void lookDown() {
		if (xrot < 90)
			xrot++;
		else
			xrot = 90;
		// printCamPos();
	}

	public void lookRight() {
		yrot += 1;
		if (yrot > 360)
			yrot -= 360;
		// printCamPos();
	}

	public void lookLeft() {
		yrot -= 1;
		if (yrot < -360)
			yrot += 360;
		// printCamPos();
	}

	@SuppressWarnings("unused")
	private void printCamPos() {
		System.out.format("Pos: %f, %f, %f\n", xpos, ypos, zpos);
		System.out.format("Ang: %f, %f\n", xrot, yrot);

	}

	public void setFog(boolean fog, float fogIntensity) {
		this.fog = fog;
		this.fogIntensity = fogIntensity;

	}

	public void setSkybox(boolean skybox2) {
		this.skybox = skybox2;
	}

	public void setFiltering(boolean linear, boolean trilinear) {
		this.linear = linear;
		this.trilinear = trilinear;
	}

	public void setLod(boolean lodOn, float lod2) {
		this.lodOn = lodOn;
		this.lod = lod2;
	}

	public void setMultiSampling(boolean multis) {
		this.multis = multis;
	}

	public void newTerrain() {
		terr = terrP.nextTerrain();
		terrN = calcTerrNormals(terr);
	}

	private float[][][] calcTerrNormals(float[][] terr) {
		int size = terr.length;
		float[][][] normals = new float[size][size][3];
		for (int i = 0; i < size - 1; i++) {
			for (int j = 0; j < size - 1; j++) {
				normals[i][j] = calcNormal(new float[] { i * hscale,
						terr[i][j] * vscale, j * hscale },
						new float[] { (i + 1) * hscale,
								terr[i + 1][j] * vscale, j * hscale },
						new float[] { (i) * hscale, terr[i][j + 1] * vscale,
								(j + 1) * hscale });
			}
		}
		return normals;
	}

	private float[] calcNormal(float[] v0, float[] v1, float[] v2) {
		float[] a = new float[] { v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2] };
		float[] b = new float[] { v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2] };
		return normalize(new float[] { a[1] * b[2] - a[2] * b[1],
				a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] });
	}
	
	private float[] normalize(float[] fs) {
		float total = 0f;
		for (int i = 0; i < fs.length; i++) {
			total += (float) Math.pow(fs[i], 2);
		}
		total = (float) Math.sqrt(total);
		for (int i = 0; i < fs.length; i++) {
			fs[i] /= total;
		}
		return fs;
	}

	public void setTerrain(boolean terrainOn) {
		this.terrainOn = terrainOn;

	}

}
